package com.nbcio.cd.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import com.nbcio.cd.domain.CdFile;
import com.nbcio.cd.domain.CdFolder;
import com.nbcio.cd.domain.CdShare;
import com.nbcio.cd.domain.vo.CdShareVo;
import com.nbcio.cd.domain.vo.CdFileVo;
import com.nbcio.cd.mapper.CdFileMapper;
import com.nbcio.cd.mapper.CdFolderMapper;
import com.nbcio.cd.mapper.CdShareMapper;
import com.nbcio.cd.service.ICdShareService;
import com.nbcio.cd.utils.CdPathUtils;
import com.nbcio.common.satoken.utils.LoginHelper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import jakarta.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.List;
import java.util.UUID;

/**
 * 云网盘分享Service业务层处理
 *
 * @author nbacheng
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class CdShareServiceImpl extends ServiceImpl<CdShareMapper, CdShare> implements ICdShareService {

    private final CdShareMapper cdShareMapper;
    private final CdFileMapper cdFileMapper;
    private final CdFolderMapper cdFolderMapper;

    /**
     * 创建分享
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public CdShareVo createShare(String shareType, Long resourceId, String shareName, String sharePassword, 
                                Date expireTime, String accessLevel, Boolean allowDownload, Boolean allowPreview) {
        try {
            // 验证资源
            validateResource(shareType, resourceId);
            
            // 检查是否已有分享
            CdShare existingShare = cdShareMapper.selectShareByResource(resourceId, shareType);
            if (existingShare != null) {
                throw new RuntimeException("该资源已有分享链接");
            }
            
            // 生成分享码
            String shareCode = generateUniqueShareCode();
            
            // 创建分享记录
            CdShare cdShare = new CdShare();
            cdShare.setShareCode(shareCode);
            cdShare.setShareType(shareType);
            cdShare.setResourceId(resourceId);
            cdShare.setShareName(shareName);
            cdShare.setSharePassword(sharePassword);
            cdShare.setAccessCount(0);
            cdShare.setDownloadCount(0);
            cdShare.setExpireTime(expireTime);
            cdShare.setIsPermanent(expireTime == null);
            cdShare.setAccessLevel(accessLevel);
            cdShare.setAllowDownload(allowDownload);
            cdShare.setAllowPreview(allowPreview);
            cdShare.setStatus("0");
            
            cdShareMapper.insert(cdShare);
            
            // 更新资源分享状态
            updateResourceShareStatus(shareType, resourceId, true, shareCode, expireTime);
            
            return buildShareVo(cdShare);
            
        } catch (Exception e) {
            log.error("创建分享失败: {}", e.getMessage(), e);
            throw new RuntimeException("创建分享失败: " + e.getMessage());
        }
    }

    /**
     * 获取分享信息
     */
    @Override
    public CdShareVo getShareInfo(String shareCode) {
        try {
            CdShare cdShare = cdShareMapper.selectShareByCode(shareCode);
            if (cdShare == null) {
                throw new RuntimeException("分享链接不存在");
            }
            
            // 检查分享是否过期
            if (!cdShare.getIsPermanent() && cdShare.getExpireTime() != null 
                && cdShare.getExpireTime().before(new Date())) {
                throw new RuntimeException("分享链接已过期");
            }
            
            // 检查分享状态
            if (!"0".equals(cdShare.getStatus())) {
                throw new RuntimeException("分享链接已失效");
            }
            
            // 更新访问次数
            cdShareMapper.updateAccessCount(cdShare.getShareId());
            
            return buildShareVo(cdShare);
            
        } catch (Exception e) {
            log.error("获取分享信息失败: {}", e.getMessage(), e);
            throw new RuntimeException("获取分享信息失败: " + e.getMessage());
        }
    }

    /**
     * 验证分享密码
     */
    @Override
    public Boolean validateSharePassword(String shareCode, String password) {
        try {
            CdShare cdShare = cdShareMapper.selectShareByCode(shareCode);
            if (cdShare == null) {
                return false;
            }
            
            // 检查是否需要密码
            if (!"password".equals(cdShare.getAccessLevel())) {
                return true;
            }
            
            // 验证密码
            return StrUtil.equals(cdShare.getSharePassword(), password);
            
        } catch (Exception e) {
            log.error("验证分享密码失败: {}", e.getMessage(), e);
            return false;
        }
    }

    /**
     * 更新分享设置
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateShareSettings(Long shareId, String shareName, String sharePassword, 
                                     Date expireTime, String accessLevel, Boolean allowDownload, Boolean allowPreview) {
        try {
            CdShare cdShare = cdShareMapper.selectById(shareId);
            if (cdShare == null) {
                throw new RuntimeException("分享不存在");
            }
            
            // 检查权限
            Long currentUserId = LoginHelper.getUserId();
            if (currentUserId == null || !currentUserId.equals(cdShare.getCreateBy())) {
                throw new RuntimeException("无权限修改此分享");
            }
            
            // 更新分享设置
            cdShare.setShareName(shareName);
            cdShare.setSharePassword(sharePassword);
            cdShare.setExpireTime(expireTime);
            cdShare.setIsPermanent(expireTime == null);
            cdShare.setAccessLevel(accessLevel);
            cdShare.setAllowDownload(allowDownload);
            cdShare.setAllowPreview(allowPreview);
            
            cdShareMapper.updateById(cdShare);
            
            // 更新资源分享状态
            updateResourceShareStatus(cdShare.getShareType(), cdShare.getResourceId(), 
                                    true, cdShare.getShareCode(), expireTime);
            
            return true;
            
        } catch (Exception e) {
            log.error("更新分享设置失败: {}", e.getMessage(), e);
            throw new RuntimeException("更新分享设置失败: " + e.getMessage());
        }
    }

    /**
     * 删除分享
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteShare(Long shareId) {
        try {
            CdShare cdShare = cdShareMapper.selectById(shareId);
            if (cdShare == null) {
                return true;
            }
            
            // 检查权限
            Long currentUserId = LoginHelper.getUserId();
            if (currentUserId == null || !currentUserId.equals(cdShare.getCreateBy())) {
                throw new RuntimeException("无权限删除此分享");
            }
            
            // 删除分享记录
            cdShareMapper.deleteById(shareId);
            
            // 更新资源分享状态
            updateResourceShareStatus(cdShare.getShareType(), cdShare.getResourceId(), 
                                    false, null, null);
            
            return true;
            
        } catch (Exception e) {
            log.error("删除分享失败: {}", e.getMessage(), e);
            throw new RuntimeException("删除分享失败: " + e.getMessage());
        }
    }

    /**
     * 获取用户分享列表
     */
    @Override
    public List<CdShareVo> getUserShares(Long userId) {
        try {
            List<CdShare> shares = cdShareMapper.selectSharesByUserId(userId);
            return shares.stream()
                    .map(this::buildShareVo)
                    .collect(java.util.stream.Collectors.toList());
            
        } catch (Exception e) {
            log.error("获取用户分享列表失败: {}", e.getMessage(), e);
            throw new RuntimeException("获取用户分享列表失败: " + e.getMessage());
        }
    }

    /**
     * 清理过期分享
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cleanExpiredShares() {
        try {
            List<CdShare> expiredShares = cdShareMapper.selectExpiredShares();
            
            for (CdShare share : expiredShares) {
                // 删除过期分享
                cdShareMapper.deleteById(share.getShareId());
                
                // 更新资源分享状态
                updateResourceShareStatus(share.getShareType(), share.getResourceId(), 
                                        false, null, null);
            }
            
            log.info("清理过期分享完成，共清理 {} 个分享", expiredShares.size());
            
        } catch (Exception e) {
            log.error("清理过期分享失败: {}", e.getMessage(), e);
        }
    }

    // ==================== 私有方法 ====================

    /**
     * 验证资源
     */
    private void validateResource(String shareType, Long resourceId) {
        if (StrUtil.isBlank(shareType) || resourceId == null) {
            throw new RuntimeException("分享类型和资源ID不能为空");
        }
        
        if ("file".equals(shareType)) {
            CdFile file = cdFileMapper.selectById(resourceId);
            if (file == null) {
                throw new RuntimeException("文件不存在");
            }
            
            // 检查权限 - 文件所有者拥有所有权限，包括分享权限
            Long userId = LoginHelper.getUserId();
            Long createBy = file.getCreateBy();
            if (createBy == null || !createBy.equals(userId)) {
                throw new RuntimeException("无权限分享此文件");
            }
        } else if ("folder".equals(shareType)) {
            CdFolder folder = cdFolderMapper.selectById(resourceId);
            if (folder == null) {
                throw new RuntimeException("文件夹不存在");
            }
            
            // 检查权限
            if (!folder.getOwnerId().equals(LoginHelper.getUserId())) {
                throw new RuntimeException("无权限分享此文件夹");
            }
        } else {
            throw new RuntimeException("不支持的分享类型");
        }
    }

    /**
     * 生成唯一分享码
     */
    private String generateUniqueShareCode() {
        String shareCode;
        int attempts = 0;
        int maxAttempts = 10;
        
        do {
            shareCode = CdPathUtils.generateShareCode();
            attempts++;
            
            if (attempts > maxAttempts) {
                throw new RuntimeException("生成分享码失败，请重试");
            }
        } while (cdShareMapper.selectShareByCode(shareCode) != null);
        
        return shareCode;
    }

    /**
     * 更新资源分享状态
     */
    private void updateResourceShareStatus(String shareType, Long resourceId, Boolean isShared, 
                                         String shareCode, Date expireTime) {
        try {
            if ("file".equals(shareType)) {
                CdFile file = cdFileMapper.selectById(resourceId);
                if (file != null) {
                    file.setIsShared(isShared);
                    file.setShareCode(shareCode);
                    file.setShareExpireTime(expireTime);
                    cdFileMapper.updateById(file);
                }
            } else if ("folder".equals(shareType)) {
                CdFolder folder = cdFolderMapper.selectById(resourceId);
                if (folder != null) {
                    folder.setIsShared(isShared);
                    folder.setShareCode(shareCode);
                    folder.setShareExpireTime(expireTime);
                    cdFolderMapper.updateById(folder);
                }
            }
        } catch (Exception e) {
            log.error("更新资源分享状态失败: {}", e.getMessage(), e);
        }
    }

    /**
     * 构建分享VO
     */
    private CdShareVo buildShareVo(CdShare cdShare) {
        CdShareVo vo = new CdShareVo();
        BeanUtil.copyProperties(cdShare, vo);
        
        // 设置分享链接
        vo.setShareUrl("/cd/share/" + cdShare.getShareCode());
        
        // 设置资源名称
        if ("file".equals(cdShare.getShareType())) {
            CdFile file = cdFileMapper.selectById(cdShare.getResourceId());
            if (file != null) {
                vo.setResourceName(file.getOriginalName());
            }
        } else if ("folder".equals(cdShare.getShareType())) {
            CdFolder folder = cdFolderMapper.selectById(cdShare.getResourceId());
            if (folder != null) {
                vo.setResourceName(folder.getFolderName());
            }
        }
        
        return vo;
    }

    @Override
    public void downloadFileByShare(String shareCode, HttpServletResponse response) {
        try {
            log.info("通过分享码下载文件，分享码: {}", shareCode);
            
            // 获取分享信息
            CdShare share = cdShareMapper.selectShareByCode(shareCode);
            if (share == null) {
                throw new RuntimeException("分享不存在或已失效");
            }
            
            // 检查分享是否过期
            if (!share.getIsPermanent() && share.getExpireTime() != null && 
                share.getExpireTime().before(new Date())) {
                throw new RuntimeException("分享已过期");
            }
            
            // 检查分享状态
            if (!"0".equals(share.getStatus())) {
                throw new RuntimeException("分享已被禁用");
            }
            
            // 检查是否允许下载
            if (!share.getAllowDownload()) {
                throw new RuntimeException("此分享不允许下载");
            }
            
            // 只支持文件下载，不支持文件夹
            if (!"file".equals(share.getShareType())) {
                throw new RuntimeException("只支持文件下载，不支持文件夹");
            }
            
            // 获取文件信息
            CdFile file = cdFileMapper.selectById(share.getResourceId());
            if (file == null) {
                throw new RuntimeException("文件不存在");
            }
            
            // 获取物理文件
            String uploadType = com.nbcio.common.core.config.RuoYiConfig.getUploadtype();
            if (!"local".equals(uploadType)) {
                throw new RuntimeException("只支持本地文件下载");
            }
            
            String profile = com.nbcio.common.core.config.RuoYiConfig.getProfile();
            String relativePath = file.getFilePath();
            
            log.info("原始filePath: {}", file.getFilePath());
            log.info("profile路径: {}", profile);
            
            // 处理路径：如果filePath以 /profile 开头，去掉它
            if (relativePath.startsWith("/profile")) {
                relativePath = relativePath.substring(8);
            }
            
            String fullPath = profile + relativePath;
            File physicalFile = new File(fullPath);
            
            log.info("处理后的相对路径: {}", relativePath);
            log.info("完整路径: {}", fullPath);
            log.info("文件是否存在: {}", physicalFile.exists());
            
            if (!physicalFile.exists()) {
                log.error("物理文件不存在，路径: {}", fullPath);
                throw new RuntimeException("文件不存在，请检查文件路径");
            }
            
            // 设置响应头
            String fileName = file.getOriginalName();
            String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8);
            
            // 设置正确的MIME类型
            String mimeType = file.getMimeType();
            if (mimeType == null || mimeType.isEmpty()) {
                // 根据文件扩展名设置MIME类型
                String extension = fileName.substring(fileName.lastIndexOf('.') + 1).toLowerCase();
                switch (extension) {
                    case "jpg":
                    case "jpeg":
                        mimeType = "image/jpeg";
                        break;
                    case "png":
                        mimeType = "image/png";
                        break;
                    case "gif":
                        mimeType = "image/gif";
                        break;
                    case "pdf":
                        mimeType = "application/pdf";
                        break;
                    case "doc":
                        mimeType = "application/msword";
                        break;
                    case "docx":
                        mimeType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
                        break;
                    case "xls":
                        mimeType = "application/vnd.ms-excel";
                        break;
                    case "xlsx":
                        mimeType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
                        break;
                    case "ppt":
                        mimeType = "application/vnd.ms-powerpoint";
                        break;
                    case "pptx":
                        mimeType = "application/vnd.openxmlformats-officedocument.presentationml.presentation";
                        break;
                    case "txt":
                        mimeType = "text/plain";
                        break;
                    default:
                        mimeType = "application/octet-stream";
                        break;
                }
            }
            
            // 设置响应头（参考正常下载方法的实现）
            response.setContentType(mimeType);
            response.setHeader("Content-Disposition", "inline; filename=\"" + encodedFileName + "\"");
            response.setHeader("Content-Length", String.valueOf(physicalFile.length()));
            
            // 写入文件内容（参考正常下载方法的实现）
            try (FileInputStream fis = new FileInputStream(physicalFile);
                 OutputStream os = response.getOutputStream()) {
                
                byte[] buffer = new byte[4096]; // 使用和正常下载相同的缓冲区大小
                int bytesRead;
                while ((bytesRead = fis.read(buffer)) != -1) {
                    os.write(buffer, 0, bytesRead);
                }
                os.flush();
            }
            
            // 更新下载次数
            cdShareMapper.updateDownloadCount(share.getShareId());
            
            log.info("通过分享码下载文件成功，文件: {}", file.getOriginalName());
            
        } catch (Exception e) {
            log.error("通过分享码下载文件失败: {}", e.getMessage(), e);
            throw new RuntimeException("下载文件失败: " + e.getMessage());
        }
    }

    @Override
    public void previewFileByShare(String shareCode, HttpServletResponse response) {
        try {
            log.info("通过分享码预览文件，分享码: {}", shareCode);
            
            // 获取分享信息
            CdShare share = cdShareMapper.selectShareByCode(shareCode);
            if (share == null) {
                throw new RuntimeException("分享不存在或已失效");
            }
            
            // 检查分享是否过期
            if (!share.getIsPermanent() && share.getExpireTime() != null && 
                share.getExpireTime().before(new Date())) {
                throw new RuntimeException("分享已过期");
            }
            
            // 检查分享状态
            if (!"0".equals(share.getStatus())) {
                throw new RuntimeException("分享已被禁用");
            }
            
            // 检查是否允许预览
            if (!share.getAllowPreview()) {
                throw new RuntimeException("此分享不允许预览");
            }
            
            // 只支持文件预览，不支持文件夹
            if (!"file".equals(share.getShareType())) {
                throw new RuntimeException("只支持文件预览，不支持文件夹");
            }
            
            // 获取文件信息
            CdFile file = cdFileMapper.selectById(share.getResourceId());
            if (file == null) {
                throw new RuntimeException("文件不存在");
            }
            
            // 获取物理文件
            String uploadType = com.nbcio.common.core.config.RuoYiConfig.getUploadtype();
            if (!"local".equals(uploadType)) {
                throw new RuntimeException("只支持本地文件预览");
            }
            
            String profile = com.nbcio.common.core.config.RuoYiConfig.getProfile();
            String relativePath = file.getFilePath();
            
            log.info("预览 - 原始filePath: {}", file.getFilePath());
            log.info("预览 - profile路径: {}", profile);
            
            // 处理路径：如果filePath以 /profile 开头，去掉它
            if (relativePath.startsWith("/profile")) {
                relativePath = relativePath.substring(8);
            }
            
            String fullPath = profile + relativePath;
            File physicalFile = new File(fullPath);
            
            log.info("预览 - 处理后的相对路径: {}", relativePath);
            log.info("预览 - 完整路径: {}", fullPath);
            log.info("预览 - 文件是否存在: {}", physicalFile.exists());
            
            if (!physicalFile.exists()) {
                log.error("预览 - 物理文件不存在，路径: {}", fullPath);
                throw new RuntimeException("文件不存在，请检查文件路径");
            }
            
            // 对于Office文档，直接生成HTML预览内容
            String fileName = file.getOriginalName().toLowerCase();
            if (fileName.endsWith(".doc") || fileName.endsWith(".docx") || 
                fileName.endsWith(".xls") || fileName.endsWith(".xlsx") ||
                fileName.endsWith(".ppt") || fileName.endsWith(".pptx")) {
                
                // 直接生成HTML预览内容，不依赖需要认证的接口
                String htmlContent = generateOfficePreviewHtml(file, physicalFile);
                
                response.setContentType("text/html;charset=UTF-8");
                response.setHeader("Cache-Control", "no-cache");
                response.getWriter().write(htmlContent);
                return;
            }
            
            // 设置预览响应头（inline而不是attachment）
            String encodedFileName = URLEncoder.encode(file.getOriginalName(), StandardCharsets.UTF_8);
            
            // 设置正确的MIME类型
            String mimeType = file.getMimeType();
            if (mimeType == null || mimeType.isEmpty()) {
                // 根据文件扩展名设置MIME类型
                String extension = file.getOriginalName().substring(file.getOriginalName().lastIndexOf('.') + 1).toLowerCase();
                switch (extension) {
                    case "jpg":
                    case "jpeg":
                        mimeType = "image/jpeg";
                        break;
                    case "png":
                        mimeType = "image/png";
                        break;
                    case "gif":
                        mimeType = "image/gif";
                        break;
                    case "pdf":
                        mimeType = "application/pdf";
                        break;
                    case "txt":
                        mimeType = "text/plain";
                        break;
                    default:
                        mimeType = "application/octet-stream";
                        break;
                }
            }
            
            // 设置预览响应头（参考正常下载方法的实现）
            response.setContentType(mimeType);
            response.setHeader("Content-Disposition", "inline; filename=\"" + encodedFileName + "\"");
            response.setHeader("Content-Length", String.valueOf(physicalFile.length()));
            
            // 写入文件内容（参考正常下载方法的实现）
            try (FileInputStream fis = new FileInputStream(physicalFile);
                 OutputStream os = response.getOutputStream()) {
                
                byte[] buffer = new byte[4096]; // 使用和正常下载相同的缓冲区大小
                int bytesRead;
                while ((bytesRead = fis.read(buffer)) != -1) {
                    os.write(buffer, 0, bytesRead);
                }
                os.flush();
            }
            
            log.info("通过分享码预览文件成功，文件: {}", file.getOriginalName());
            
        } catch (Exception e) {
            log.error("通过分享码预览文件失败: {}", e.getMessage(), e);
            try {
                response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                response.setCharacterEncoding("UTF-8");
                String errorHtml = "<html><head><meta charset='UTF-8'></head><body><h1>预览失败</h1><p>" + e.getMessage() + "</p></body></html>";
                response.getWriter().write(errorHtml);
            } catch (Exception ignored) {
            }
        }
    }

    @Override
    public CdFileVo getFileInfoByShare(String shareCode) {
        try {
            log.info("通过分享码获取文件信息，分享码: {}", shareCode);
            
            // 获取分享信息
            CdShare share = cdShareMapper.selectShareByCode(shareCode);
            if (share == null) {
                throw new RuntimeException("分享不存在或已失效");
            }
            
            // 检查分享是否过期
            if (!share.getIsPermanent() && share.getExpireTime() != null && 
                share.getExpireTime().before(new Date())) {
                throw new RuntimeException("分享已过期");
            }
            
            // 检查分享状态
            if (!"0".equals(share.getStatus())) {
                throw new RuntimeException("分享已被禁用");
            }
            
            // 只支持文件，不支持文件夹
            if (!"file".equals(share.getShareType())) {
                throw new RuntimeException("只支持文件，不支持文件夹");
            }
            
            // 获取文件信息
            CdFile file = cdFileMapper.selectById(share.getResourceId());
            if (file == null) {
                throw new RuntimeException("文件不存在");
            }
            
            // 构建文件VO
            CdFileVo fileVo = new CdFileVo();
            BeanUtil.copyProperties(file, fileVo);
            
            // 设置预览URL（使用分享预览接口）
            fileVo.setPreviewUrl("/cd/share/preview/" + shareCode);
            
            log.info("通过分享码获取文件信息成功，文件: {}", file.getOriginalName());
            
            return fileVo;
            
        } catch (Exception e) {
            log.error("通过分享码获取文件信息失败: {}", e.getMessage(), e);
            throw new RuntimeException("获取文件信息失败: " + e.getMessage());
        }
    }
    
    /**
     * 生成Office文档预览HTML
     */
    private String generateOfficePreviewHtml(CdFile file, File physicalFile) {
        StringBuilder html = new StringBuilder();
        html.append("<!DOCTYPE html><html><head><meta charset='UTF-8'>");
        html.append("<title>").append(file.getOriginalName()).append("</title>");
        html.append("<style>");
        html.append("body { font-family: Arial, sans-serif; padding: 20px; line-height: 1.6; }");
        html.append(".header { border-bottom: 2px solid #409EFF; padding-bottom: 10px; margin-bottom: 20px; }");
        html.append(".content { white-space: pre-wrap; word-wrap: break-word; }");
        html.append(".warning { background: #fff3cd; border: 1px solid #ffc107; padding: 15px; border-radius: 4px; margin: 20px 0; }");
        html.append("</style></head><body>");
        html.append("<div class='header'><h1>").append(file.getOriginalName()).append("</h1></div>");
        html.append("<div class='content'>");

        String fileName = file.getOriginalName().toLowerCase();

        try {
            if (fileName.endsWith(".docx")) {
                html.append(readDocxContent(physicalFile));
            } else if (fileName.endsWith(".doc")) {
                html.append("<div class='warning'>Word 2003 格式(.doc)暂不支持在线预览，请下载后使用 Word 打开或转换为 .docx 格式。</div>");
            } else if (fileName.endsWith(".xlsx") || fileName.endsWith(".xls")) {
                html.append(readExcelContent(physicalFile));
            } else if (fileName.endsWith(".pptx")) {
                html.append("<div class='warning'>PowerPoint 文件暂不支持在线预览，请下载后使用 PowerPoint 打开。</div>");
            } else if (fileName.endsWith(".ppt")) {
                html.append("<div class='warning'>此文件格式暂不支持在线预览，请下载后使用 PowerPoint 打开。</div>");
            } else {
                html.append("<div class='warning'>此文件类型暂不支持在线预览，请下载后使用相应软件打开。</div>");
            }
        } catch (Exception e) {
            log.error("生成Office预览HTML失败: {}", e.getMessage(), e);
            html.append("<div class='warning'>无法读取文件内容: ").append(e.getMessage()).append("</div>");
        }

        html.append("</div></body></html>");
        return html.toString();
    }
    
    /**
     * 读取DOCX文档内容
     */
    private String readDocxContent(File file) {
        try {
            FileInputStream fis = new FileInputStream(file);
            XWPFDocument document = new XWPFDocument(fis);
            
            StringBuilder content = new StringBuilder();
            for (XWPFParagraph paragraph : document.getParagraphs()) {
                content.append(paragraph.getText()).append("\n");
            }
            
            document.close();
            fis.close();
            return content.toString();
        } catch (Exception e) {
            log.error("读取DOCX文档失败: {}", e.getMessage(), e);
            return "<div class='warning'>无法读取DOCX文档内容: " + e.getMessage() + "</div>";
        }
    }
    
    /**
     * 读取Excel文档内容
     */
    private String readExcelContent(File file) {
        try {
            FileInputStream fis = new FileInputStream(file);
            Workbook workbook;

            if (file.getName().toLowerCase().endsWith(".xls")) {
                workbook = new HSSFWorkbook(fis); // Excel 2003
            } else {
                workbook = new XSSFWorkbook(fis); // Excel 2007+
            }

            StringBuilder content = new StringBuilder();
            Sheet sheet = workbook.getSheetAt(0);
            int maxRows = Math.min(sheet.getLastRowNum() + 1, 100);

            for (int i = 0; i < maxRows; i++) {
                Row row = sheet.getRow(i);
                if (row != null) {
                    for (int j = 0; j < row.getLastCellNum(); j++) {
                        Cell cell = row.getCell(j);
                        if (cell != null) {
                            content.append(getCellValue(cell)).append("\t");
                        }
                    }
                    content.append("\n");
                }
            }
            workbook.close();
            fis.close();
            return content.toString();
        } catch (Exception e) {
            log.error("读取Excel文档失败: {}", e.getMessage(), e);
            return "<div class='warning'>无法读取Excel文档内容: " + e.getMessage() + "</div>";
        }
    }
    
    /**
     * 获取单元格值
     */
    private String getCellValue(Cell cell) {
        switch (cell.getCellType()) {
            case STRING:
                return cell.getStringCellValue();
            case NUMERIC:
                // 简化处理，不检查日期格式
                return String.valueOf(cell.getNumericCellValue());
            case BOOLEAN:
                return String.valueOf(cell.getBooleanCellValue());
            case FORMULA:
                return cell.getCellFormula();
            default:
                return "";
        }
    }
}
